﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Volunteering.model {
  public class SpecialityRepository {
    private readonly List<Speciality> Specialities = new List<Speciality>();

    private readonly Regex regex = new Regex(@"[,，\s]");

    private readonly Dictionary<Char, Subjects> SubjectMap = new Dictionary<Char, Subjects> {
      {'物', Subjects.物理 },
      {'史', Subjects.历史},
      {'政', Subjects.政治},
      {'地', Subjects.地理},
      {'化', Subjects.化学},
      {'生', Subjects.生物}
    };

    public SpecialityRepository(List<(string, string, string, string, string, string)> list) {
      this.Specialities = list.SelectMany(t => {
        var discipline = t.Item1.Trim();
        var major = t.Item2.Trim();
        var specialities = regex.Split(t.Item3).Where(r => !string.IsNullOrEmpty(r)).ToList();
        var main = t.Item4.Trim().ToCharArray().Select(c => SubjectMap[c]).ToHashSet();
        var ext = t.Item5.Trim().ToCharArray().Select(c => SubjectMap[c]).ToHashSet();

        var s = t.Item6.Trim();
        Subjects? essential = null;

        if (!string.IsNullOrEmpty(s))
          essential = SubjectMap[s[0]];

        return specialities.Select(name => new Speciality(name, discipline, major, main, ext, essential));
      }).ToList();
    }

    public List<Speciality> FindAll() => Specialities;

    public Speciality FindByName(string name) => Specialities.First(s => s.Name.Equals(name));

    public List<Speciality> FindByKeys(string keys) {
      if (string.IsNullOrWhiteSpace(keys)) return Specialities;

      var keyList = regex.Split(keys).Where(k => !string.IsNullOrEmpty(k)).ToList();
      return Specialities.Where(s => keyList.Any(k => s.Name.Contains(k))).ToList();
    }

    public List<Speciality> FindBySubjects(Subjects main, HashSet<Subjects> ext) {
      // 查询必定是2科，但要求可能是1科、2科、4科，其中可能还有1科必选：
      // - 当要求只有1科时，查询科目必须包含该科目才算匹配：
      // - 当要求固定2科时，查询科目必须完全相同才算匹配；
      // - 当要求为3或4科时，查询科目必须属于要求范围才算匹配：
      // - 任何条件下，查询科目必须包含必选科目。

      if (ext.Count != 2)
        return new List<Speciality>();
      else
        return Specialities.Where(s => s.MainSubjects.Contains(main)
          && ((s.ExtSubjects.Count == 1 && ext.Contains(s.ExtSubjects.First()))
              || (s.ExtSubjects.Count == 2 && ext.SetEquals(s.ExtSubjects))
              || (s.ExtSubjects.IsSupersetOf(ext)))
          && (!s.EssentialSubject.HasValue || ext.Contains(s.EssentialSubject.Value))
        ).ToList();
    }
  }
}